/**
 * @file tester.c
 * @author zhaitao (zhaitao.as@outlook.com)
 * @brief
 * @version 0.1
 * @date 2023-05-17
 *
 * @copyright zhaitao.as@outlook.com (c) 2023
 *
 */
#include "tester.h"
#include "bsp.h"
#include "main.h"

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "fatfs.h"
#include "lauxlib.h"
#include "lua.h"
#include "lualib.h"
#include "usb_device.h"

#include "RA8876.h"
#include "bsp.h"
#include "bsp_luaep.h"
#include "bus.h"
#include "dwt_delay.h"
#include "lua_ssd2828.h"
#include "shell_export.h"
#include "util.h"

#include "bitmap_info.h"
#include "lua_ra8876.h"
#include "util.h"

#include "lua_func_lib.h"

#define MAX_BMP_FILES 32
#define MAX_IMAGE_FILE_LEN 32
#define DEFAULT_IMAGE_CHANGE_INTERVAL 800
#define blockSize 10
#define GRamSize (1920 * 3 * blockSize)

extern SD_HandleTypeDef hsd;
extern UART_HandleTypeDef huart1;
// extern SRAM_HandleTypeDef hsram1;

const char Title[] = { " \r\n\
 _____            _     _   __\r\n\
(_   _)          ( )_ /  )  _  \\\r\n\
  | |    __   ___|  _)_  | ( ) |\r\n\
  | |  / __ \\  __) |   | | | | |\r\n\
  | | (  ___/__  \\ |_  | | (_) |\r\n\
  (_)  \\____)____/\\__) (_)\\___/\r\n\
   \r\n\
Copyright (c) zhaitao.as@outlook.com\r\n\
tel: +86-18604124670\r\n\
|||||||||||||||||||||||||||||||||||||\r\n\
\r\n\
" };

const char test_type_folder_names[][16] = {
	"IMG_DISP",
	"IMG_ASM",
	"IMG_COG",
};

lua_State *L;
__attribute__((section(".ccmram"))) uint8_t lineData[GRamSize] = { 0 };

struct Tester test10 = {
	.version = { VERSION_MAJOR, VERSION_MINOR, VERSION_Fix },
	.gram_addr = lineData,
	.gram_size = GRamSize,
	.img_count = 0,
	.debug = 0,
	.auto_power_off = 1,
	.img_interval_ms = DEFAULT_IMAGE_CHANGE_INTERVAL,
	.ssd2828 = { 0 },
};
FATFS fs;
uint8_t uartRxBuffer;
uint8_t getUartFlag = 0;

uint8_t lvds_color_mode;

void set_lvds_color_mode(uint16_t v)
{
	lvds_color_mode = v;
}

/**
 * @brief get image (bmp, jpg) file name from sdcard
 *
 * @param path folder path
 * @param ifn image file names buffer
 * @return int file number
 */
int getSDCardImageFileNames(char *path, char *ifn[])
{
	FILINFO info;
	DIR file_dir;
	FRESULT fr;
	int cnt = 0;

	fr = f_opendir(&file_dir, path);
	if (fr) {
		printf("FATFS f_opendir ERROR\r\n");
		Error_Handler();
	}

	while (1) {
		fr = f_readdir(&file_dir, &info);

		if (fr || info.fname[0] == 0)
			break;
		if (info.fname[0] == '.')
			continue;
		if (strstr(info.fname, ".bmp") || strstr(info.fname, ".jpg")) {
			// only get file name in number
			char fileName[32];
			strcpy(fileName, info.fname);
			char *pch;
			pch = strtok(fileName, ".");
			if (atoi(pch)) {
				strcpy(ifn[cnt], info.fname);
				cnt++;
			}
		}
	}

	return cnt;
}

void RA8876_Fill_Rect(uint16_t X1, uint16_t Y1, uint16_t X2, uint16_t Y2,
		      struct RA8876_Pixel_Mode2 *pixel)
{
}

uint8_t bmpDrawFile(char *fileName,
		    void (*fill_rect)(uint16_t, uint16_t, uint16_t, uint16_t,
				      const uint16_t *))
{
	int __error = 0;
	uint8_t BMP_Header[54];
	uint16_t width, height;
	uint16_t *pd;
	FIL fil;
	FRESULT res;
	uint32_t br;

	uint32_t lineStartPosition = 0;
	uint32_t lineDrawed = 0;
	uint32_t lineToCache = 0;
	uint32_t dataDrawed = 0;
	uint32_t dataToDraw = 0;
	uint32_t filesize;
	uint32_t lineCountGRamCache;
	uint32_t lineDataSize;

	BITMAPFILEHEADER *fh;
	BITMAPINFOHEADER *ih;

	printf("bmpDrawFile: %s \r\n", fileName);

	res = f_open(&fil, (const TCHAR *)fileName, FA_READ | FA_OPEN_EXISTING);
	if (res != FR_OK) {
		printf("bmpDrawFile f_open error %d\r\n", res);
		__error = 2;
		goto bmpDrawFile_close;
	}

	//
	res = f_read(&fil, BMP_Header, 54, (UINT *)&br);
	if (res != FR_OK) {
		printf("bmpDrawFile file read error\r\n");
		__error = 3;
		goto bmpDrawFile_close;
	}

	fh = (BITMAPFILEHEADER *)BMP_Header;
	ih = (BITMAPINFOHEADER *)(BMP_Header + 14);

	if (fh->bFileType != 0x4D42) {
		printf("bmpDrawFile file type should 0x4D42, get: %04x\r\n",
		       fh->bFileType);
		__error = 1;
		goto bmpDrawFile_close;
	}

	width = ih->bImageWidth;
	height = ih->bImageHeight;
	lineDataSize = width * 3; /* width * 3, In Byte */
	/* The pixel format is defined by the DIB header or Extra bit masks.
	Each row in the Pixel array is padded to a multiple of 4 bytes in size*/
	if (lineDataSize % 4) {
		lineDataSize += (4 - (lineDataSize % 4));
	}

	filesize = fh->bPixelDataOffset + lineDataSize * height;
	lineCountGRamCache = GRamSize / lineDataSize;

	while (lineDrawed < height) {
		/* Calculate cache size */
		if ((lineDrawed + lineCountGRamCache) < height) {
			// 没到图片结尾, 缓存gram所能容纳的最大行数
			lineToCache = lineCountGRamCache;
		} else {
			// 到了结尾, 只需要缓存一部分行
			lineToCache = height - lineDrawed;
		}

		/* 	Calculate new cache area start position
			bmp图片是y翻转存放的, 文件头之后是图片最后一行, 文件结尾是图片的第一行
			为了保准观感上刷图是从屏的0行开始, 要先从文件结尾部分开始刷
			先找到文件结尾, 再减去已经刷过的行, 再减去这一次要刷的行, 就是新的起点
		*/
		dataDrawed = lineDrawed * lineDataSize;
		dataToDraw = lineToCache * lineDataSize;
		br = filesize - dataDrawed - dataToDraw;

		/* Move Pointer to position of new cache */
		if (f_lseek(&fil, br)) {
			printf("bmpDrawFile f_lseek error\r\n");
			__error = 3;
			goto bmpDrawFile_close;
		}

		HAL_GPIO_WritePin(ULED3_GPIO_Port, ULED3_Pin, GPIO_PIN_RESET);
		/* Read Line Data */
		if (f_read(&fil, lineData, dataToDraw, (UINT *)&br)) {
			printf("bmpDrawFile f_read error\r\n");
			__error = 4;
			goto bmpDrawFile_close;
		}
		HAL_GPIO_WritePin(ULED3_GPIO_Port, ULED3_Pin, GPIO_PIN_SET);

		HAL_GPIO_WritePin(ULED2_GPIO_Port, ULED2_Pin, GPIO_PIN_RESET);
		/* Draw cached data, line by line */
		for (uint32_t i = 0; i < lineToCache; i++) {
			lineStartPosition =
				(lineToCache - 1 - i) * lineDataSize;
			pd = (uint16_t *)(lineData + lineStartPosition);

			// 如果 flag 为 1，转换为 JEIDA 格式
			if (lvds_color_mode == 1) {
				convert_to_jeida_format((uint8_t *)pd,
							lineDataSize);
			}

			fill_rect(0, lineDrawed + i, width, 1, pd);
		}
		HAL_GPIO_WritePin(ULED2_GPIO_Port, ULED2_Pin, GPIO_PIN_SET);

		lineDrawed += lineToCache;
	}

bmpDrawFile_close:

	if (f_close(&fil)) {
		printf("bmpDrawFile f_close error\r\n");
	}

	return __error;
}

void fillColor(uint32_t color)
{
	for (uint16_t y = 0; y < test10.timing.va; y++) {
		for (uint16_t x = 0; x < test10.timing.ha; x++) {
			RA8876_pixel(color);
		}
	}
}
/**
 * @brief draw
 *
 * @param fn file name
 * @return uint8_t 0:success, !0:error
 */
uint8_t drawFile(char *fn)
{
	uint8_t r = 0;

	RA8876_GraphicDrawingMode();

	HAL_GPIO_WritePin(ULED2_GPIO_Port, ULED2_Pin, GPIO_PIN_RESET);

	// run normaly
	if (strstr(fn, ".bmp")) {
		bmpDrawFile(fn, RA8876_MPU16_24bpp_Mode1_Memory_Write);
	} else if (strstr(fn, ".jpg")) {
		// jpgDrawFile(fn);
		printf("JPG file not support\r\n");
	} else {
		r = -1;
	}

	HAL_GPIO_WritePin(ULED2_GPIO_Port, ULED2_Pin, GPIO_PIN_SET);

	return r;
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	UNUSED(huart);
	// HAL_UART_Transmit(&huart1, &rd, 1, 0xfff);
	getUartFlag = 1;
	// HAL_UART_Receive_IT(&huart1, &rd, 1);
}

int get_prod_folder_select(void)
{
	int swb0 = HAL_GPIO_ReadPin(SWB0_GPIO_Port, SWB0_Pin);
	int swb1 = HAL_GPIO_ReadPin(SWB1_GPIO_Port, SWB1_Pin);

	if ((swb0 == 0) && (swb1 == 0)) {
		return 0;
	} else if ((swb0 == 0) && (swb1 == 1)) {
		return 1;
	} else if ((swb0 == 1) && (swb1 == 0)) {
		return 2;
	} else if ((swb0 == 1) && (swb1 == 1)) {
		return 3;
	} else {
		return -1;
	}
}

int get_test_type_folder_select(void)
{
	int swb2 = HAL_GPIO_ReadPin(SWB2_GPIO_Port, SWB2_Pin);
	int swb3 = HAL_GPIO_ReadPin(SWB3_GPIO_Port, SWB3_Pin);

	if ((swb2 == 0) && (swb3 == 0)) {
		return 0;
	} else if ((swb2 == 0) && (swb3 == 1)) {
		return 1;
	} else if ((swb2 == 1) && (swb3 == 0)) {
		return 2;
	} else if ((swb2 == 1) && (swb3 == 1)) {
		return 3;
	} else {
		return -1;
	}
}

int testModeStart(void)
{
	DIR dir;
	FRESULT res;
	FILINFO fno;
	const char root_path[] = "";
	const char lcm_prod_folder_prefix[] = "LCM_PROD_";
	char imageFileNames[MAX_BMP_FILES][MAX_IMAGE_FILE_LEN];
	char *imageFileSorted[MAX_BMP_FILES];

	int swb0 = HAL_GPIO_ReadPin(SWB0_GPIO_Port, SWB0_Pin);
	int swb1 = HAL_GPIO_ReadPin(SWB1_GPIO_Port, SWB1_Pin);
	int swb2 = HAL_GPIO_ReadPin(SWB2_GPIO_Port, SWB2_Pin);
	int swb3 = HAL_GPIO_ReadPin(SWB3_GPIO_Port, SWB3_Pin);
	printf("SW: %d%d%d%d\r\n", swb3, swb2, swb1, swb0);

	int lcm_prod_folder_cnt = 0;
	int result = 0;

	HAL_GPIO_WritePin(RA8876_RST_GPIO_Port, RA8876_RST_Pin, GPIO_PIN_RESET);
	HAL_Delay(100);

	HAL_GPIO_WritePin(RA8876_RST_GPIO_Port, RA8876_RST_Pin, GPIO_PIN_SET);
	HAL_Delay(10);

	DWT_Init();

	printf(Title);

	/* Check sdcard */
	fs_check_sdcard(&fs);

	/* Switch Folder */
	char path[32];
	char path_require[32];
	strcpy(path, root_path);

	// Open root directory
	res = f_opendir(&dir, path);
	if (res == FR_OK) {
		for (;;) {
			res = f_readdir(&dir, &fno); // Read a directory item
			if (res != FR_OK || fno.fname[0] == 0)
				break; // Break on error or end of dir
			if (fno.fattrib & AM_DIR) { // It is a directory

				if (strstr(fno.fname, lcm_prod_folder_prefix)) {
					lcm_prod_folder_cnt += 1;
				}
			}
		}
		f_closedir(&dir);
	} else {
		printf("f_opendir error \r\n");
	}

	if (lcm_prod_folder_cnt == 0) {
		// compatiable with lagency folder structure
		strcpy(path, root_path);
	} else {
		// use new folder structe
		sprintf(path, "%s%d/%s", lcm_prod_folder_prefix,
			get_prod_folder_select(),
			test_type_folder_names[get_test_type_folder_select()]);
		sprintf(path_require, "%s%d/display.lua",
			lcm_prod_folder_prefix, get_prod_folder_select());
	}

	printf("path is %s\r\n", path);

	/* Get sdcard file names and sort*/
	do {
		for (int c = 0; c < MAX_BMP_FILES; c++) {
			imageFileSorted[c] = imageFileNames[c];
		}
		test10.img_count =
			getSDCardImageFileNames(path, imageFileSorted);

		sortStrings(imageFileSorted, test10.img_count);

		printf("[LCMTEST] Sorted Image Files:\r\n");
		for (uint8_t i = 0; i < test10.img_count; i++) {
			printf("\t%s ", imageFileSorted[i]);
			if ((i > 0) && (i % 8 == 0)) {
				printf("\r\n");
			}
		}
	} while (0);

	/* Initial Shell */
	/*do {
		ntshell_init(
		    &ntshell,
		    func_read,
		    func_write,
		    func_callback,
		    (void *)&ntshell);
		ntshell_set_prompt(&ntshell, "LCMTEST>");
	} while (0);*/

	/* LCD VDD On */
	HAL_GPIO_WritePin(CTRL_5V0_GPIO_Port, CTRL_5V0_Pin, GPIO_PIN_SET);
	HAL_GPIO_WritePin(CTRL_3V3_GPIO_Port, CTRL_3V3_Pin, GPIO_PIN_SET);
	HAL_GPIO_WritePin(CTRL_1V8_GPIO_Port, CTRL_1V8_Pin, GPIO_PIN_SET);
	HAL_Delay(10);

	/* Run main.lua */
	do {
		L = luaL_newstate();
		if (L == NULL) {
			printf("[Lua] Error cannot create state: not enough memory\r\n");
			Error_Handler();
		}
		printf("[Lua] Create New State Success\r\n");

		luaL_openlibs(L);
		luaopen_base(L);
		luaL_setfuncs(L, FunctionOfLcmtest10, 0);
		luaL_setfuncs(L, functionExport_ssd2828, 0);
		luaL_setfuncs(L, functionExport_Bus, 0);
		luaL_setfuncs(L, functionExport_bsp, 0);
		luaL_setfuncs(L, FunctionOfRA8876, 0);

		char lua_file_path[64];
		sprintf(lua_file_path, "%s/1.lua", path);
		runLua(L, lua_file_path);
		sprintf(lua_file_path, "%s/2.lua", path);
		runLua(L, lua_file_path);
		sprintf(lua_file_path, "%s/3.lua", path);
		runLua(L, lua_file_path);
		sprintf(lua_file_path, "%s/4.lua", path);
		runLua(L, lua_file_path);
		sprintf(lua_file_path, "%s/5.lua", path);
		runLua(L, lua_file_path);
		sprintf(lua_file_path, "%s/main.lua", path);
		runLua(L, lua_file_path);

		HAL_GPIO_WritePin(ULED3_GPIO_Port, ULED3_Pin, GPIO_PIN_SET);
		lua_close(L);
	} while (0);

	/* Main Loop With Key Pause */
	test10.flag_pause = 0;
	uint32_t timePiece = test10.img_interval_ms + 1;
	test10.img_index = 0;
	HAL_UART_Receive_IT(&huart1, &uartRxBuffer, 1);
	while (1) {
		/* Detect Uart Interrupt, then excute shell */
		/*if (getUartFlag) {
			HAL_GPIO_TogglePin(ULED3_GPIO_Port, ULED3_Pin);
			//printf("get uart interrupt %02x\r\n", rd);
			getUartFlag = 0;
			HAL_UART_Receive_IT(&huart1, &uartRxBuffer, 1);
			vtrecv_execute(&(ntshell.vtrecv), &uartRxBuffer, sizeof(uartRxBuffer));
		}*/

		/* Detect Key State, and Change test10.flag_pause */
		if (HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin) ==
		    GPIO_PIN_RESET) {
			HAL_Delay(20);
			while (HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin) ==
			       GPIO_PIN_RESET) {
			};

			if (test10.flag_pause == 0) {
				test10.flag_pause = 1;
				HAL_GPIO_WritePin(ULED3_GPIO_Port, ULED3_Pin,
						  GPIO_PIN_RESET);
			} else {
				test10.flag_pause = 0;
				HAL_GPIO_WritePin(ULED3_GPIO_Port, ULED3_Pin,
						  GPIO_PIN_SET);
				timePiece = test10.img_interval_ms +
					    1; /* change image immediately */
			}
		}

		/* Detect pause flag, if flag, timePiece++, else keep remain */
		if (test10.flag_pause == 0) {
			timePiece++;
		}

		/* Detect timePiece, if timePiece >= 1000, change display image,
		 * and make timePiece 0*/
		if (timePiece >= test10.img_interval_ms) {
			timePiece = 0;

			if (test10.img_index >= test10.img_count) {
				if (test10.auto_power_off == 0) {
					test10.img_index = 0;
				} else {
					HAL_Delay(1000);

					// RGB Signal off
					Display_OFF();
					HAL_Delay(50);

					// IO goto 0
					HAL_GPIO_WritePin(UIO_2_GPIO_Port,
							  UIO_2_Pin,
							  GPIO_PIN_RESET);
					HAL_GPIO_WritePin(UIO_3_GPIO_Port,
							  UIO_3_Pin,
							  GPIO_PIN_RESET);
					HAL_GPIO_WritePin(UIO_4_GPIO_Port,
							  UIO_4_Pin,
							  GPIO_PIN_RESET);
					HAL_GPIO_WritePin(UIO_5_GPIO_Port,
							  UIO_5_Pin,
							  GPIO_PIN_RESET);
					HAL_GPIO_WritePin(UIO_14_GPIO_Port,
							  UIO_14_Pin,
							  GPIO_PIN_RESET);
					HAL_GPIO_WritePin(UIO_15_GPIO_Port,
							  UIO_15_Pin,
							  GPIO_PIN_RESET);
					HAL_GPIO_WritePin(UIO_16_GPIO_Port,
							  UIO_16_Pin,
							  GPIO_PIN_RESET);
					HAL_Delay(10);

					// VDD off
					HAL_GPIO_WritePin(CTRL_5V0_GPIO_Port,
							  CTRL_5V0_Pin,
							  GPIO_PIN_RESET);
					HAL_GPIO_WritePin(CTRL_3V3_GPIO_Port,
							  CTRL_3V3_Pin,
							  GPIO_PIN_RESET);
					HAL_GPIO_WritePin(CTRL_1V8_GPIO_Port,
							  CTRL_1V8_Pin,
							  GPIO_PIN_RESET);
					while (1)
						;
				}
			}

			// drawFile(imageFileSorted[test10.img_index]);

			sprintf(test10.img_full_path, "%s/%s", path,
				imageFileSorted[test10.img_index]);
			drawFile(test10.img_full_path);

			test10.img_index++;
		}

		/* time piece interval = 1ms */
		HAL_Delay(1);
	}

	return result;
}

int usbModeStart(void)
{
	int result = 0;
	if (HAL_SD_Init(&hsd) != HAL_OK) {
		Error_Handler();
	}
	if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK) {
		Error_Handler();
	}
	printf("SUCCESS SDCARD INITIAL\r\n");

	MX_USB_DEVICE_Init();
	printf("SUCCESS USB MODE, ENTERN LOOP, READ(LED2), WRITE(LED3)\r\n");
	while (1) {
	}

	return result;
}
